<template>
  <div class="tinymce-box">
    <Editor v-model="content" :api-key="apiKey" :init="init" id="basic-example" />
  </div>
</template>

<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, computed, watch, nextTick, watchEffect } from "vue";
import type { ComputedRef } from 'vue'
import type { RawEditorOptions } from 'tinymce/tinymce'
import Editor from '@tinymce/tinymce-vue'
import tinymceInstance from 'tinymce/tinymce'
import 'tinymce/themes/silver/theme'
import 'tinymce/icons/default'
import 'tinymce/models/dom'
// 插件
// import 'tinymce/plugins/powerpaste'  付费插件
import 'tinymce/plugins/preview'
import 'tinymce/plugins/searchreplace'
import 'tinymce/plugins/autolink'
import 'tinymce/plugins/directionality'
import 'tinymce/plugins/visualblocks'
import 'tinymce/plugins/visualchars'
import 'tinymce/plugins/fullscreen'
import 'tinymce/plugins/image'
import 'tinymce/plugins/link'
import 'tinymce/plugins/media'
import 'tinymce/plugins/code'
import 'tinymce/plugins/codesample'
import 'tinymce/plugins/table'
import 'tinymce/plugins/charmap'
import 'tinymce/plugins/pagebreak'
import 'tinymce/plugins/nonbreaking'
import 'tinymce/plugins/insertdatetime'
import 'tinymce/plugins/advlist'
import 'tinymce/plugins/lists'
import 'tinymce/plugins/wordcount'
import 'tinymce/plugins/autosave'
import 'tinymce/plugins/emoticons/plugin.min'
import 'tinymce/plugins/emoticons/js/emojis.min'
import 'tinymce/plugins/anchor'

// 官网文档地址：https://www.tiny.cloud/docs/tinymce/6/

/**
 * @description 类型注解
 * @IEmitProps emit 事件
 * @ToolbarGroup 编辑器工具栏配置
 * @Iprops 父组件传递的数据
 */
interface IEmitProps {
  (e: 'update:value', val: string): void
}
interface ToolbarGroup {
  name?: string
  items: string[]
}
interface Iprops {
  value: string
  // 是否只读
  readonly?: boolean
  toolbar?: string | boolean | string[] | ToolbarGroup[]
  plugins?: string | string[]
  height?: number | string
  width?: number | string
  removedMenuitems?: string
}

// 初始化 编辑器
onMounted(() => {
  tinymceInstance.init({})
})

// 组件销毁
onBeforeUnmount(() => {
  // 销毁对 DOM 的所有内部引用以解决内存泄漏问题
  if (tinymceInstance?.activeEditor?.destroy) {
    tinymceInstance.activeEditor.destroy()
  }
})

// 设置 props 默认值
const props = withDefaults(defineProps<Iprops>(), {
  readonly: false,
  toolbar: () => [
    'blocks fontsize | bold italic underline strikethrough subscript superscript blockquote lineheight | forecolor backcolor removeformat | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | emoticons link image media table | charmap hr insertdatetime pagebreak | cut copy paste pastetext | selectall visualblocks searchreplace | undo redo restoredraft | print preview fullscreen',
  ],
  plugins:
    'emoticons preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media code codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount autosave',
  height: 500,
  width: '100%',
  removedMenuitems: 'newdocument codesample anchor code fullscreen fontfamily fontsize',
})

// 编辑器输入值
const content = ref(props.value)

// tinymce 秘钥
const apiKey = ref('jmo5tbgjyyzxnk9rv18wktvly6sts1k4d0mq5dxzamxvox09')

// init 参数
const init: ComputedRef<RawEditorOptions> = computed(() => {
  return {
    selector: 'textarea#basic-example',
    // skin路径
    skin_url: '/tinymce/skins/ui/oxide',
    // 语言包的路径
    language_url: '/tinymce/langs/zh-Hans.js',
    // 语言类型
    language: 'zh-Hans',
    placeholder: '请输入...',
    min_width: 320,
    min_height: 220,
    // 编辑器宽度
    width: props.width,
    // 编辑器高度
    height: props.height,
    // 编辑器宽高是否可变，false-否,true-高可变，'both'-宽高均可
    resize: 'both',
    // tiny技术支持信息是否显示
    branding: false,
    // 统计字数栏是否显示
    statusbar: true,
    // 编辑器获取焦点是否添加蓝色轮廓
    highlight_on_focus: true,
    // 是否显示升级促销按钮
    promotion: false,
    font_size_formats: '10px 11px 12px 14px 16px 18px 20px 24px 36px',
    // 自定义 editor 样式
    content_style: 'p, div {font-size: 14px; margin: 0px; border:0px ; padding: 0px}',
    content_css: '/tinymce/skins/content/default/content.css',
    // 插件
    plugins: props.plugins,
    // 'emoticons preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media code codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount autosave', // 插件配置 axupimgs indent2em
    // 工具栏
    toolbar: props.toolbar,
    // [
    //   'blocks fontsize | bold italic underline strikethrough subscript superscript blockquote lineheight | forecolor backcolor removeformat | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | emoticons link image media table | charmap hr insertdatetime pagebreak | cut copy paste pastetext | selectall visualblocks searchreplace | undo redo restoredraft | print preview fullscreen',
    // ],
    // 移除菜单栏 选项 'newdocument codesample anchor code fullscreen fontfamily fontsize'
    removed_menuitems: props.removedMenuitems,
    // 图片是否可粘贴
    paste_data_images: true,
    // 保留粘贴样式 默认为 none
    paste_webkit_styles: 'all',
    /**
     * 是否启用合并格式功能
     * 但如果保留嵌套或相同的格式元素很重要，则可以禁用此选项 可选：true / false
     * 主要用来提升性能，减少不必要的 DOM
     */
    paste_merge_formats: true,
    // 上传文件类型 结合 file_picker_callback 使用
    file_picker_types: 'media',
    // 编辑器初始化完成回调
    init_instance_callback: (editor: any) => {
      console.log(`Editor: ${editor.id} 初始化完成`)
    },
    // 此处为图片上传处理函数
    // eslint-disable-next-line @typescript-eslint/no-unused-vars
    images_upload_handler: (blobInfo: any, _progress: Function) =>
      new Promise((resolve) => {
        const formData = new FormData()
        //   formData.append('file', blobInfo.blob(), blobInfo.filename())
        formData.append('file', blobInfo.blob())
        let reader = new FileReader() // 本地预览
        reader.readAsDataURL(blobInfo.blob())
        reader.onloadend = function () {
          const imgbase64 = reader.result as string
          // 请求成功之后调用 resolve
          resolve(imgbase64)
        }
      }),
    file_picker_callback: (callback: Function, _value: any, meta: any) => {
      // Provide alternative source and posted for the media dialog
      // 创建文件选择
      let inputElem:HTMLInputElement = document.createElement('input')
      inputElem.setAttribute('type', 'file')
      inputElem.setAttribute('accept', 'media')
      inputElem.click()
      inputElem.onchange = () => {
        // 获取文件信息
        let file = inputElem.files![0]
        const formData = new FormData()
        formData.append('file', file)
        // 所有都转成base64文件流,来自官方文档https://www.tiny.cloud/docs/configure/file-image-upload/#file_picker_callback
        let reader = new FileReader()
        reader.readAsDataURL(file)
        reader.onload = function () {
          // Note: Now we need to register the blob in TinyMCEs image blob
          // registry. In the next release this part hopefully won't be
          // necessary, as we are looking to handle it internally.
          //   let id = 'blobid' + new Date().getTime()
          //   let blobCache = window.tinymce.activeEditor.editorUpload.blobCache
          //   let base64 = (reader.result as string).split(',')[1]
          //   console.log('file', file)
          //   console.log('base64', base64)
          //   let blobInfo = blobCache.create(
          //     id,
          //     'https://gossv.cfp.cn/videos/mts_videos/medium/VCG2221152458.mp4',
          //     base64
          //   )
          //   blobCache.add(blobInfo)
          // call the callback and populate the Title field with the file name
          // callback(blobInfo.blobUri(), { title: file.name })
          if (meta.filetype === 'media') {
            // 请求成功之后调用 callback
            // eslint-disable-next-line n/no-callback-literal
            callback(
              'https://web-assets.dcloud.net.cn/unidoc/zh/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317%20(3).mp4',
              { title: file.name }
            )
          }
        }
      }
    },
  }
})

// const init = reactive<RawEditorOptions>({
//   selector: 'textarea#basic-example',
//   // skin路径
//   skin_url: '/tinymce/skins/ui/oxide',
//   // 语言包的路径
//   language_url: '/tinymce/langs/zh-Hans.js',
//   // 语言类型
//   language: 'zh-Hans',
//   placeholder: '请输入...',
//   min_width: 320,
//   min_height: 220,
//   // 编辑器宽度
//   width: props.width,
//   // 编辑器高度
//   height: props.height,
//   // 编辑器宽高是否可变，false-否,true-高可变，'both'-宽高均可
//   resize: 'both',
//   // tiny技术支持信息是否显示
//   branding: false,
//   // 统计字数栏是否显示
//   statusbar: true,
//   // 编辑器获取焦点是否添加蓝色轮廓
//   highlight_on_focus: true,
//   // 是否显示升级促销按钮
//   promotion: false,
//   font_size_formats: '10px 11px 12px 14px 16px 18px 20px 24px 36px',
//   // 自定义 editor 样式
//   content_style: 'p, div {font-size: 14px; margin: 0px; border:0px ; padding: 0px}',
//   content_css: '/tinymce/skins/content/default/content.css',
//   // 插件
//   plugins: props.plugins,
//   // 'emoticons preview searchreplace autolink directionality visualblocks visualchars fullscreen image link media code codesample table charmap pagebreak nonbreaking anchor insertdatetime advlist lists wordcount autosave', // 插件配置 axupimgs indent2em
//   // 工具栏
//   toolbar: props.toolbar,
//   // [
//   //   'blocks fontsize | bold italic underline strikethrough subscript superscript blockquote lineheight | forecolor backcolor removeformat | alignleft aligncenter alignright alignjustify outdent indent | bullist numlist | emoticons link image media table | charmap hr insertdatetime pagebreak | cut copy paste pastetext | selectall visualblocks searchreplace | undo redo restoredraft | print preview fullscreen',
//   // ],
//   // 移除菜单栏 选项 'newdocument codesample anchor code fullscreen fontfamily fontsize'
//   removed_menuitems: props.removedMenuitems,
//   // 图片是否可粘贴
//   paste_data_images: true,
//   // 保留粘贴样式 默认为 none
//   paste_webkit_styles: 'all',
//   /**
//    * 是否启用合并格式功能
//    * 但如果保留嵌套或相同的格式元素很重要，则可以禁用此选项 可选：true / false
//    * 主要用来提升性能，减少不必要的 DOM
//    */
//   paste_merge_formats: true,
//   // 上传文件类型 结合 file_picker_callback 使用
//   file_picker_types: 'media',
//   // 编辑器初始化完成回调
//   init_instance_callback: (editor: any) => {
//     console.log(`Editor: ${editor.id} 初始化完成`)
//   },
//   // 此处为图片上传处理函数
//   // eslint-disable-next-line @typescript-eslint/no-unused-vars
//   images_upload_handler: (blobInfo: any, _progress: Function) =>
//     new Promise((resolve) => {
//       const formData = new FormData()
//       //   formData.append('file', blobInfo.blob(), blobInfo.filename())
//       formData.append('file', blobInfo.blob())
//       let reader = new FileReader() // 本地预览
//       reader.readAsDataURL(blobInfo.blob())
//       reader.onloadend = function () {
//         const imgbase64 = reader.result as string
//         // 请求成功之后调用 resolve
//         resolve(imgbase64)
//       }
//     }),
//   file_picker_callback: (callback: Function, _value: any, meta: any) => {
//     // Provide alternative source and posted for the media dialog
//     // 创建文件选择
//     let inputElem = document.createElement('input')
//     inputElem.setAttribute('type', 'file')
//     inputElem.setAttribute('accept', 'media')
//     inputElem.click()
//     inputElem.onchange = () => {
//       // 获取文件信息
//       let file = inputElem.files[0]
//       const formData = new FormData()
//       formData.append('file', file)
//       // 所有都转成base64文件流,来自官方文档https://www.tiny.cloud/docs/configure/file-image-upload/#file_picker_callback
//       let reader = new FileReader()
//       reader.readAsDataURL(file)
//       reader.onload = function () {
//         // Note: Now we need to register the blob in TinyMCEs image blob
//         // registry. In the next release this part hopefully won't be
//         // necessary, as we are looking to handle it internally.
//         //   let id = 'blobid' + new Date().getTime()
//         //   let blobCache = window.tinymce.activeEditor.editorUpload.blobCache
//         //   let base64 = (reader.result as string).split(',')[1]
//         //   console.log('file', file)
//         //   console.log('base64', base64)
//         //   let blobInfo = blobCache.create(
//         //     id,
//         //     'https://gossv.cfp.cn/videos/mts_videos/medium/VCG2221152458.mp4',
//         //     base64
//         //   )
//         //   blobCache.add(blobInfo)
//         // call the callback and populate the Title field with the file name
//         // callback(blobInfo.blobUri(), { title: file.name })
//         if (meta.filetype === 'media') {
//           // 请求成功之后调用 callback
//           // eslint-disable-next-line n/no-callback-literal
//           callback(
//             'https://web-assets.dcloud.net.cn/unidoc/zh/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20200317%20(3).mp4',
//             { title: file.name }
//           )
//         }
//       }
//     }
//   },
// })

// 监听 是否只读
watch(
  () => props.readonly,
  (val) => {
    nextTick(() => {
      if (tinymceInstance?.activeEditor?.mode?.set && val) {
        tinymceInstance?.activeEditor.mode.set('readonly')
      }
    })
  },
  { immediate: true }
)

// 定义 emits
const emits = defineEmits<IEmitProps>()

// 内容有变化，就更新内容，将值返回给父组件
watchEffect(() => {
  emits('update:value', content.value)
})
</script>

<style scoped>
.tinymce-box > textarea {
  display: none;
}
</style>
<style>
.tox.tox-tinymce {
  max-width: 100%;
}
</style>
